#ifndef SESSION_HPP
#define SESSION_HPP
#include "util.hpp"
#include <unordered_map> // Include for std::unordered_map
typedef enum
{
    UNLOGIN,
    LOGIN
} ss_statu;
class session
{
private:
    uint64_t _ssid;            // 标识符
    uint64_t _uid;             // session对应的用户id
    ss_statu _statu;           // 用户状态:登录 未登录
    wsserver_t::timer_ptr _tp; // 定时器

public:
    session(uint64_t ssid) : _ssid(ssid), _statu(UNLOGIN) { DLOG("session %p create", this); }
    ~session() { DLOG("session %lu destroy", _ssid); }
    void set_statu(ss_statu statu) { _statu = statu; }
    uint64_t ssid() { return _ssid; }
    void set_user(uint64_t uid) { _uid = uid; }
    uint64_t get_user() { return _uid; }
    bool is_login() { return _statu == LOGIN; }
    void set_timer(const wsserver_t::timer_ptr &tp) { _tp = tp; }
    wsserver_t::timer_ptr &get_timer() { return _tp; }
};
#define SESSION_TIMEOUT 30000
#define SESSION_FOREVER -1
using session_ptr = std::shared_ptr<session>; // session对象的智能指针
class session_manager
{
private:
    uint64_t _next_ssid; // session id计数器
    std::mutex _mutex;
    std::unordered_map<uint64_t, session_ptr> _session; // session id和session对象的映射关系
    wsserver_t *_server;

public:
    session_manager(wsserver_t *server) : _next_ssid(1), _server(server) { DLOG("session manager create"); }
    ~session_manager() { DLOG("session manager destroy"); }
    session_ptr create_session(uint64_t uid, ss_statu statu) // 创建session对象
    {
        std::unique_lock<std::mutex> lock(_mutex);
        session_ptr ssp(new session(_next_ssid));
        ssp->set_statu(statu); // 设置session状态
        ssp->set_user(uid);
        // 管理session对象
        _session.insert(std::make_pair(_next_ssid, ssp)); // session id和session对象的映射关系
        _next_ssid++;
        return ssp;
    }
    void append_session(const session_ptr &ssp) // 添加session对象
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _session.insert(std::make_pair(ssp->ssid(), ssp));        
    }
    session_ptr get_session_by_ssid(uint64_t ssid) // 获取session对象
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _session.find(ssid);
        if (it == _session.end())
        {
            return session_ptr();
        }
        return it->second;
    }

    void remove_session(uint64_t ssid)                      // 移除session对象
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _session.find(ssid);
        if (it != _session.end())
        {
            _session.erase(it);
        }
    }
    void set_session_expire_time(uint64_t ssid, int ms) // 依赖于websocketpp的定时器,参数是ms
    {
        // 在http通信的时候(登录,注册) session应该具备生命周期,指定时间无通信之后应该被删除
        // 但是在客户端建立websocket长连接之后,session对象应该是永久存在的
        // 登录之后,创建session,session需要在指定时间五通信之后删除
        // 在进入游戏大厅或者游戏房间之后,session对象应该是永久存在的
        // 等到退出游戏大厅或者游戏房间,这个session就应该重新设置为临时,在长时间无通信之后删除
        session_ptr ssp = get_session_by_ssid(ssid);
        if (ssp.get() ==  nullptr)
        {
            return;
        }    
        wsserver_t::timer_ptr tp = ssp->get_timer();
        // 1.在session对象永久存在的情况下,设置永久存在
        
        if(tp.get() == nullptr && ms == SESSION_FOREVER){ // 已经是永久存在
            return;        
        }
        else if(tp.get() == nullptr && ms != SESSION_FOREVER){ // 之前是永久存在,现在设置了定时删除
            // 2.在session对象永久存在的情况下,设置指定时间之后被删除的定时任务
            wsserver_t::timer_ptr tmp_tp = _server->set_timer \
            (ms,std::bind(&session_manager::remove_session, this, ssid));
            ssp->set_timer(tmp_tp); // 设置定时任务
        }
        
        
        else if(tp.get() != nullptr && ms == SESSION_FOREVER){ // 之前是定时删除,现在设置为永久存在
            // 3.在session设置了定时删除的情况下,将session设置为永久存在
            tp->cancel();  // 取消定时任务,但是不是立刻执行的,所以在添加的时候需要使用定时器,而不是立刻添加.
            // 取消定时任务,会立即执行,需要再添加一次这个session对象
            ssp->set_timer(wsserver_t::timer_ptr()); // 将session关联的定时器设置为空
            // 重新添加这个session对象
            _server->set_timer(0,std::bind(&session_manager::append_session, this, ssp));  
        }
        else if(tp.get() != nullptr && ms != SESSION_FOREVER){ // 之前是定时删除,现在设置了新的定时删除
            // 4.在session设置了定时删除的情况下,将session重置删除时间  
            tp->cancel(); // 取消定时任务,但是不是立刻执行的,所以在添加的时候需要使用定时器,而不是立刻添加.
            ssp->set_timer(wsserver_t::timer_ptr()); 
            _server->set_timer(0,std::bind(&session_manager::append_session, this, ssp)); // 重新添加这个session对象

            wsserver_t::timer_ptr tmp_tp = _server->set_timer \
            (ms,std::bind(&session_manager::remove_session, this, ssp->ssid()));
            ssp->set_timer(tmp_tp); // 重新设置session关联的定时器
        }
        

    }

};

#endif